package com.hup.utils.io.audio.sourceDataLine;

import lombok.extern.log4j.Log4j2;

import javax.sound.sampled.*;
import javax.sound.sampled.DataLine.Info;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.ReentrantLock;

/**
 * SourceDataLine播放类
 * <pre>
 *  负责流的创建,关闭,读写(播放),重置等
 *  https://blog.csdn.net/clj198606061111/article/details/52648805
 * </pre>
 */
@Log4j2
class SourceDataLineBundle implements AutoCloseable {

    private final SourceDataPlayTask task;
    private AudioInputStream audioInputStream;
    private SourceDataLine sourceDataLine;

    public SourceDataLineBundle(SourceDataPlayTask task)
            throws IOException, UnsupportedAudioFileException, LineUnavailableException {
        this.task = task;
        InputStream ips = getClass().getResourceAsStream(task.getAudioInfo().getResourcePath());
        audioInputStream = AudioSystem.getAudioInputStream(ips);

        AudioFormat audioFormat = task.getAudioInfo().getAudioFormat();
        Info info = new Info(SourceDataLine.class, audioFormat);
        sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
        sourceDataLine.open(audioFormat, task.getBufferSize());
        sourceDataLine.start();
    }

    /**
     * 读取流数据:负责正常读取流,或实现重新播放/循环播放
     */
    protected int readFormAudio(byte[] buff) throws IOException {
        int readLength = audioInputStream.read(buff);
        if ((readLength == -1 && task.isLoop())) {
            //重新播放,或结束后循环:重置流对象
            return resetAndRead(buff);
        } else if (task.isRestarting()) {
            ReentrantLock lock = task.getLock();
            try {
                lock.lock();
                task.setRestarting(false);
                return resetAndRead(buff);
            } finally {
                lock.unlock();
            }
        }
        return readLength;
    }

    /**
     * 尝试用重置流,实现重新播放/循环播放
     *
     * @return readLength
     */
    private int resetAndRead(byte[] buff) {
        int readLength;
        try {
            log.debug("尝试重置流");
                /*
                尝试了直接修改流对象中的属性,去实现复用,最后都失败了
                要audioInputStream达到reset后read成功,就要重置BufferedInputStream.markpos
                Field fStream = AudioInputStream.class.getDeclaredField("stream");
                Field fMarkpos = BufferedInputStream.class.getDeclaredField("markpos");
                Field fFramePos = AudioInputStream.class.getDeclaredField("framePos");
                audioInputStream.mark(Integer.MAX_VALUE);
                audioInputStream.reset();
                 */
                /*
                直接重建流对象,实现[重置播放],不是复用流
                没深究性能/资源问题
                 */
            audioInputStream.close();
            InputStream ips = getClass().getResourceAsStream(task.getAudioInfo().getResourcePath());
            audioInputStream = AudioSystem.getAudioInputStream(ips);
            readLength = audioInputStream.read(buff);
            log.debug("重置流成功, readLength={}", readLength);
        } catch (Exception e) {
            readLength = -1;
            log.warn("重置流错误", e);
        }
        return readLength;
    }

    protected void writeToLine(byte[] buff, int readLength) {
        //经试验,只要buff数据是对的,sourceDataLine是可以一直使用的
        sourceDataLine.write(buff, 0, readLength);
    }

    @Override
    public void close() throws Exception {
        audioInputStream.close();

        //这n行没深究
        sourceDataLine.flush();
        sourceDataLine.drain();
        sourceDataLine.stop();
        sourceDataLine.close();
    }

}